package co.fitstart.mobile.web.interceptor;

import java.io.IOException;
import java.util.Map;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerMapping;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import org.springframework.web.util.WebUtils;

import com.demo2do.core.BusinessException;

import co.fitstart.mobile.Constant;

/**
 * 
 * @author mohanlan
 *
 */
public class RouterInterceptor extends HandlerInterceptorAdapter {
    
    private static final Logger logger = LoggerFactory.getLogger(RouterInterceptor.class);
    
    private static final String USER_AGENT = "User-Agent";
    
    private static final String MICRO_MESSENGER = "MicroMessenger";
    
    private static final String SAVED_REQUEST = "WECHAT_WEB_BASE_AUTH_SAVED_REQUEST";
    
    /**
     * wechat openid key
     */
    private String openidKey = Constant.OPENID_KEY;
    
    /**
     * if user agent is pc, just return to target url
     */
    private String targetUrl;
    
    /**
     * WeChat openid key stored in the cookie
     */
    private String wechatBaseAuthUrl;
    
    /**
     * pc host 
     */
    private String websiteHost;
    
    /**
     * @param wechatBaseAuthUrl
     */
    public void setWechatBaseAuthUrl(String wechatBaseAuthUrl) {
        this.wechatBaseAuthUrl = wechatBaseAuthUrl;
    }
    
    /**
     * TargetUrl the targetUrl to set
     * @param targetUrl
     */
    public void setTargetUrl(String targetUrl) {
        this.targetUrl = targetUrl;
    }
    
    /**
     * websiteHost the websiteHost to set
     * @param websiteHost
     */
    public void setWebsiteHost(String websiteHost) {
        this.websiteHost = websiteHost;
    }
    
    /*
     * (non-Javadoc)
     * @see org.springframework.web.servlet.handler.HandlerInterceptorAdapter#preHandle(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, java.lang.Object)
     */
    @SuppressWarnings("rawtypes")
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {
        
        if (handler instanceof HandlerMethod) {
            
            if(logger.isDebugEnabled()) {
                logger.debug("RouterInterceptor#preHandle - To determine pc or mobile url .. ");
            }
            
            HandlerMethod handlerMethod = (HandlerMethod) handler;
            
            Router router = AnnotationUtils.findAnnotation(handlerMethod.getMethod(), Router.class);
            
            // Check1 router ---- if without router or router property ignore is true, return super preHandle
            if(router == null || router.ignore()) {
                logger.info("RouterInterceptor#preHandle - RETURN SUPER_PREHANLDER ---------------- (router is null or router ignore is true)");
                return super.preHandle(request, response, handler);
            }
                        
            // Begin ---- start check and deside go to mobile or pc
            logger.info("RouterInterceptor#preHandle - ############################Begin RouterInteceptor.");
            
            // 1 get origin request url from http servlet request
            String requestUrl = request.getRequestURL().toString();
            logger.info("RouterInterceptor#preHandle - get origin request url [" + requestUrl + "]");
            
            // 2. generate target url by router & config properities (router's targetUrl > config properity's targetUrl > config properity's websiteHost)
            String targetUrl = StringUtils.isBlank(router.targetUrl()) ? ( this.targetUrl == null ? this.websiteHost : this.targetUrl) : router.targetUrl();
            logger.info("RouterInterceptor#preHandle - get target url [" + targetUrl + "]");
            
            Map pathVariables = (Map) request.getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE);
            if(StringUtils.isNotBlank(targetUrl) && StringUtils.equalsIgnoreCase(targetUrl, "/products/detail")) {
                targetUrl = "/products/" + pathVariables.get("productId").toString();
            }
            
            // 3 get userAgent & determine pc or mobile by userAgent, requestUrl, targetUrl
            String userAgent = request.getHeader(USER_AGENT);
            
            String determineUrl = this.determineUrl(userAgent, requestUrl, targetUrl);
            logger.info("RouterInterceptor#preHandle - get determine url [" + determineUrl + "]");
            
            // 4 determine url == origin request url, return super preHandle (MOBILE)
            if(StringUtils.equalsIgnoreCase(requestUrl, determineUrl)) {
                
                logger.info("RouterInterceptor#preHandle - determine url same as origin request url");
                
                // 4.1 check and save openid into cookie 
                if(this.checkOpenid(userAgent, request, response, handler)) {

                    logger.trace("RouterInterceptor#preHandle - openid not exit in cookie do wechat base auth.");
                    this.saveOriginalRequest(request);
                    
                    try {
                        
                        String resolveBaseAuthUrl = this.wechatBaseAuthUrl.startsWith("http") ? 
                                                    wechatBaseAuthUrl : (com.demo2do.core.web.utils.WebUtils.getRealServerPath(request) + wechatBaseAuthUrl);
                        
                        logger.trace("RouterInterceptor#preHandle - redirect to wechat base auth url [{}]", resolveBaseAuthUrl);
                        
                        response.sendRedirect(resolveBaseAuthUrl);
                        return false;
                        
                    } catch (IOException e) {
                        logger.error("RouterInterceptor#preHandle - redirect to wechat base auth url occurs error", e);
                        throw new BusinessException("微信基本授权失败");
                    }
                }else {
                    return super.preHandle(request, response, handler);
                }
                
            } else {  
                
                // 5 determine url != origin request url, return determine url (PC)
                
                // 5.1 get final resolved target url & redirect
                String resolveTargetUrl = targetUrl.startsWith("http") ? targetUrl : (websiteHost + targetUrl);
                
                logger.info("RouterInterceptor#preHandle - redirect to determine url [{}]", resolveTargetUrl);
                
                response.sendRedirect(resolveTargetUrl);
                
                return false;
            }
            
        }
        
        return super.preHandle(request, response, handler);
    }
    
    /**
     * 
     * @param request
     * @param response
     * @param originUrl
     * @return
     */
    private String determineUrl(String userAgent, String originUrl, String targetUrl) {
        
        if(userAgent.indexOf("Mobile") != -1) {
            logger.info("RouterInterceptor#determineUrl - redirect to mobile url");
            return originUrl;
            
        } else {
            logger.info("RouterInterceptor#determineUrl - redirect to pc url");
            return targetUrl;
        }
    }
    
    /**
     * Check userAgent and save openid into cookie when userAgent is microMessenger
     * 
     * @param userAgent
     * @param request
     * @param response
     */
    private boolean checkOpenid(String userAgent, HttpServletRequest request, HttpServletResponse response, Object handler) {
        
        // 1. check userAgent is MicroMessenger or not
        logger.trace("RouterInterceptor#checkOpenid - check cureent userAgent [{}] is microMessager or not.", userAgent);
        boolean isMicroMessenger = StringUtils.isNotBlank(userAgent) && userAgent.indexOf(MICRO_MESSENGER) >= 0;
        
        // 2. if userAgent is MicroMessenger and cookie openid not exit
        if(isMicroMessenger && !this.hasOpenid(request, this.openidKey)) {
            logger.trace("RouterInterceptor#checkOpenid - current userAgent is microMessenger ======== true");
            return true;
        }else {
            logger.trace("RouterInterceptor#checkOpenid - current userAgent is microMessenger ======== false");
            return false;
        }
        
    }
    

    /**
     * Check openid in cookie by openidKey & request
     * 
     * @param request
     * @param response
     * @param wechatClient
     * @return
     */
    private boolean hasOpenid(HttpServletRequest request, String openidKey) {
        
        // 2. start get openid from cookie by openid ket
        logger.info("RouterInterceptor#hasOpenid - start get openid from cookie by openid key");
        
        // 3.1 get openid from cookie by openid key
        Cookie openidCookie = WebUtils.getCookie(request, openidKey);
        if (openidCookie == null) {
            logger.warn("RouterInterceptor#hasOpenid - get null openid from cookie with openidKey [{}]", openidKey);
            return false;
        }
        
        logger.info("RouterInterceptor#hasOpenid - get openid from cookie user mobile into cookie with key [{}] and value [{}]", openidKey, openidCookie.getValue());
        return true;
    }
    
    /**
     * Save original request in HTTP session for further reference
     * 
     * @param request
     */
    private void saveOriginalRequest(HttpServletRequest request) {
        
        // Construct original URL from request URI and query string
        String originalUri = com.demo2do.core.web.utils.WebUtils.getRealRequestURI(request);
        String queryString = request.getQueryString();
        
        String originalUrl = StringUtils.isBlank(queryString) ? originalUri : (originalUri + "?" + queryString);
        
        // Save the original URL in the session currently
        HttpSession httpSession = request.getSession();
        httpSession.setAttribute(SAVED_REQUEST, originalUrl);
    }
}
